/**
 * @file tasks.c
 * @author Linquan (you@domain.com)
 * @brief init code
 * @version 1.0
 * @date 2024-02-20
 * 
 * @copyright Copyright (c) 2024
 * 
 */

#include "tasks.h"
#include <inttypes.h> // PRId64
#include "SEGGER_RTT.h"
#include "RTm.h"


#ifdef TEST_TASKS
    struct tasks_t *tests;
#endif


#define GET_TIME	GetSysTime()



static void do_tests(struct tasks_t *tasks);

void tasks_init(struct tasks_t *tasks, int ntask) {
    INIT_LIST_HEAD(&tasks->tasks);
    int bytes = (ntask * sizeof(struct task_t)) + (ntask * sizeof(void*)) + sizeof(size_t);
    void *buf = malloc(bytes);
    nodebuf_init(&tasks->taskbuf, buf, bytes, sizeof(struct task_t));
    do_tests(tasks);
}

void tasks_init_withbuf(struct tasks_t *tasks, void *buf, int buflen) {
    INIT_LIST_HEAD(&tasks->tasks);
    nodebuf_init(&tasks->taskbuf, buf, buflen, sizeof(struct task_t));
    do_tests(tasks);
}

void task_fini(struct tasks_t *tasks) {
    nodebuf_fini(&tasks->taskbuf, tasks->needfree ? free : NULL);
}

void tasks_add_task(struct tasks_t *tasks, 
                    int delay_ms, task_fn *fn, int i1, int i2, int i3)
{
    
    DEBUG_LOG_INFO(DEBUG_LEVEL_2, "---into tasks_add_task---\n");
    
    struct task_t *task = (struct task_t *) nodebuf_malloc(&tasks->taskbuf, 0);
   	
	if(delay_ms > 0)
        task->time_ms = GET_TIME + delay_ms;
    task->fn = fn;
    task->args.i1 = i1;
    task->args.i2 = i2;
    task->args.i3 = i3;

    list_add_tail(&task->list, &tasks->tasks);
}

int tasks_remove_task(struct tasks_t *tasks, task_fn *fn, int once) {
    int n = 0;
    struct list_head *task_, *safe_;
    list_for_each_safe(task_, safe_, &tasks->tasks) {
        struct task_t *task = (struct task_t *)task_;
        if (task->fn == fn) {
            list_del(task_);
            nodebuf_free(&tasks->taskbuf, task);
            n++;
            if (once) break;
        }
    }
    return n;
}

void tasks_schedule(struct tasks_t *tasks, int64_t now_ms) 
{
    struct list_head *task_, *safe_;
    list_for_each_safe(task_, safe_, &tasks->tasks) {
        struct task_t *task = (struct task_t *)task_;
        if (task->fn && (task->time_ms <= now_ms)) {
            int r_ms = task->fn(&task->args);
            if (r_ms > 0) {
                task->time_ms = GET_TIME + r_ms;
            } else {
                list_del(task_);
                nodebuf_free(&tasks->taskbuf, task);
            }
        }
    }
}

void tasks_print_all(struct tasks_t *tasks, int64_t now_ms, const char *name) {
    struct list_head *task_;
    int i = 1, count = 0;
    list_for_each(task_, &tasks->tasks) { count++; }
    printf("%s tasks (count=%d now=%"PRId64"):\n", name, count, now_ms);

    list_for_each(task_, &tasks->tasks) {
        struct task_t *task = (struct task_t *)task_;
        printf("  task%d: time_ms=%"PRId64"(%+"PRId64") fn=%p i1=%d i2=%d i3=%d\n",
               i++, task->time_ms, (now_ms - task->time_ms), task->fn,
               task->args.i1, task->args.i2, task->args.i3);
    }
}


static void do_tests(struct tasks_t *tasks) {
#ifdef TEST_TASKS
    // just for test
    tests = tasks;
    tasks_add_task(tests, 0, task_dummy, 11, 12, 13);
    tasks_add_task(tests, 5000, task_hello, 1, 2, 3);
    tasks_add_task(tests, 0, task_dummy, 31, 32, 33);
    tasks_print_all(tests, time_ms(), "tests");
#endif
}
